/***********************************************************
		Group			:		G2
		Project         :       Big Table
		File Name       :       Map.java
		Author			:		
		Purpose         :       
		Change History  :
 ***********************************************************/
package BigT;

import global.AttrType;
import global.Convert;
import heap.FieldNumberOutOfBoundException;
import heap.InvalidMapSizeException;
import heap.InvalidTypeException;

import java.io.IOException;

public class Map {

	/**
	 * Maximum size of any map
	 */
	public static final int fixed_size = 100;

	/**
	 * a byte array to hold data
	 */
	public byte[] mapData;

	/**
	 * start position of this Map in data[]
	 */
	private int map_offset;

	/**
	 * length of this Map
	 */
	private int map_length = fixed_size;

	/**
	 * 
	 */
	private int timeStamp;

	/**
	 * 
	 */
	private String rowLabel;

	/**
	 * 
	 */
	private String columnLabel;

	/**
	 * 
	 */
	private String value;

	/**
	 * private field Number of fields in this Map
	 */
	private short fldCnt = 4;

	/**
	 * private field Array of offsets of the fields
	 */
	public int getLength() {
		return map_length;
	}

	private short[] fldOffset = new short[5];

	public Map() {
		mapData = new byte[fixed_size];
		map_offset = 0;
		map_length = fixed_size;
	}

	public Map(int size) {
		// Creat a new tuple
		mapData = new byte[size];
		map_offset = 0;
		map_length = size;
	}

	public Map(byte[] amap, int offset) {

		this.mapData = amap;
		map_offset = offset;
	}

	Map(Map fromMap) {

		this.mapData = fromMap.getMapByteArray();
		map_offset = 0;
		// fldCnt = fromMap.noOfFlds();
		// fldOffset = fromMap.copyFldOffset();
	}

	// Function by Preetika Tyagi
	public String getRowLabel() throws IOException,
			FieldNumberOutOfBoundException {
		String rowLabel = null;
		int fldNo = 1;
		if ((fldNo > 0) && (fldNo <= fldCnt)) {
			rowLabel = Convert.getStrValue(0, mapData, 32); // strlen+2

		}

		return rowLabel;
	}

	// Function by Preetika Tyagi
	public String getColumnLabel() throws IOException,
			FieldNumberOutOfBoundException {
		String columnLabel = null;
		int fldNo = 2;
		if ((fldNo > 0) && (fldNo <= fldCnt)) {
			columnLabel = Convert.getStrValue(32, mapData, 32); // strlen+2

		}
		return columnLabel;
	}

	// Function by Preetika Tyagi
	public int getTimeStamp() throws IOException,
			FieldNumberOutOfBoundException {

		int timeStamp = 0;
		int fldNo = 3;
		if ((fldNo > 0) && (fldNo <= fldCnt)) {
			timeStamp = Convert.getIntValue(64, mapData); // strlen+2

		}
		return timeStamp;

	}

	// Function by Preetika Tyagi
	public String getValue() throws IOException, FieldNumberOutOfBoundException {

		String value = null;
		int fldNo = 4;
		if ((fldNo > 0) && (fldNo <= fldCnt)) {
			value = Convert.getStrValue(68, mapData, 32); // strlen+2

		}

		return value;
	}

	// Function by Preetika Tyagi
	public Map setRowLabel(String val) {
		int fldNo = 1;
		try {
			Convert.setStrValue(val, 0, mapData);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return this;
	}

	// Function by Preetika Tyagi
	public Map setColumnLabel(String val) {
		int fldNo = 2;
		try {
			Convert.setStrValue(val, 32, mapData);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return this;
	}

	// Function by Preetika Tyagi
	public Map setTimeStamp(int val) {
		int fldNo = 3;
		try {
			Convert.setIntValue(val, 64, mapData);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return this;
	}

	// Function by Preetika Tyagi
	public Map setValue(String val) {
		int fldNo = 4;
		try {
			Convert.setStrValue(val, 68, mapData);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return this;
	}

	// public int size() {
	// return map_length;
	// }
	public short size() {
		return ((short) (fldOffset[fldCnt] - map_offset));
	}

	public void mapCopy(Map fromMap) {
		byte[] temparray = fromMap.getMapByteArray();
		System.arraycopy(temparray, 0, mapData, map_offset, map_length);
	}

	public void mapInit(byte[] amap, int offset) {
		mapData = amap;
		map_offset = offset;

	}

	public void mapSet(byte[] frommap, int offset) {
		System.arraycopy(frommap, offset, mapData, 0, fixed_size);
		map_offset = 0;
	}

	public byte[] getMapByteArray() {
		byte[] mapcopy = new byte[map_length];
		System.arraycopy(this.mapData, map_offset, mapcopy, 0,
				this.mapData.length);
		return mapcopy;
	}

	/** *******Doing for the test purpose *********************** */
	public byte[] returnMapByteArray() {
		return mapData;
	}

	/**
	 * get the offset of a tuple
	 * 
	 * @return offset of the tuple in byte array
	 */
	public int getOffset() {
		return map_offset;
	}

	// Added By Preetika Tyagi to fix offsets of all fields in a map
	public void setHdr() throws IOException, InvalidTypeException,
			InvalidMapSizeException {
		short numFlds = 4;
		AttrType[] types = new AttrType[4];
		types[0] = new AttrType(AttrType.attrString);
		types[1] = new AttrType(AttrType.attrString);
		types[2] = new AttrType(AttrType.attrInteger);
		types[3] = new AttrType(AttrType.attrString);

		short[] strSizes = new short[3];
		strSizes[0] = 30;
		strSizes[1] = 30;
		strSizes[2] = 30;

		/*
		 * if ((numFlds + 2) * 2 > max_size) throw new
		 * InvalidTupleSizeException(null, "TUPLE: TUPLE_TOOBIG_ERROR");
		 */
		fldCnt = numFlds;
		// Convert.setShortValue(numFlds, map_offset, mapData);
		// fldOffset = new short[numFlds + 1];
		// int pos = map_offset + 2; // start position for fldOffset[]

		// sizeof short =2 +2: array siaze = numFlds +1 (0 - numFilds) and
		// another 1 for fldCnt
		// fldOffset[0] = (short) ((numFlds + 2) * 2 + map_offset);
		fldOffset[0] = 0;
		// Convert.setShortValue(fldOffset[0], pos, mapData);
		// pos += 2;
		short strCount = 0;
		short incr;
		int i;

		for (i = 1; i < numFlds; i++) {
			switch (types[i - 1].attrType) {

			case AttrType.attrInteger:
				incr = 4;
				break;

			case AttrType.attrReal:
				incr = 4;
				break;

			case AttrType.attrString:
				incr = (short) (strSizes[strCount] + 2); // strlen in bytes =
				// strlen +2
				strCount++;
				break;

			default:
				throw new InvalidTypeException(null, "TUPLE: TUPLE_TYPE_ERROR");
			}
			fldOffset[i] = (short) (fldOffset[i - 1] + incr);
			// Convert.setShortValue(fldOffset[i], pos, mapData);
			// pos += 2;

		}
		switch (types[numFlds - 1].attrType) {

		case AttrType.attrInteger:
			incr = 4;
			break;

		case AttrType.attrReal:
			incr = 4;
			break;

		case AttrType.attrString:
			incr = (short) (strSizes[strCount] + 2); // strlen in bytes =
			// strlen +2
			break;

		default:
			throw new InvalidTypeException(null, "TUPLE: TUPLE_TYPE_ERROR");
		}
		fldOffset[numFlds] = (short) (fldOffset[i - 1] + incr);
		// Convert.setShortValue(fldOffset[numFlds], pos, mapData);
		// System.out.println("MAP DATA LENGTH IN SET HEADER: " +
		// mapData.length);
		fldOffset[0] = 0;
		fldOffset[1] = 31;
		fldOffset[2] = 61;
		fldOffset[3] = 65;
		map_length = fldOffset[numFlds] - map_offset;
		/*
		 * if (tuple_length > max_size) throw new
		 * InvalidTupleSizeException(null, "TUPLE: TUPLE_TOOBIG_ERROR");
		 */
	}

	// Function by Preetika Tyagi
	public void print() {
		try {
			System.out.println(this.getRowLabel() + "\t\t\t\t"
					+ this.getColumnLabel() + "\t\t\t\t" + this.getTimeStamp()
					+ "\t\t\t\t" + this.getValue());
		} catch (FieldNumberOutOfBoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void tupleSet(byte[] record, int offset, int length) {
		System.arraycopy(record, offset, mapData, 0, length);
		map_offset = 0;
		map_length = length;
	}
}
